package cz.czu.pef.DataModelling.beans;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.el.Expression;

import org.richfaces.component.Filterable;
import org.richfaces.component.Sortable2;
import org.richfaces.model.DataProvider;
import org.richfaces.model.ExtendedFilterField;
import org.richfaces.model.FilterField;
import org.richfaces.model.Ordering;
import org.richfaces.model.SortField2;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;

import cz.czu.pef.DataModelling.dao.impl.CustomFilterField;
import cz.czu.pef.DataModelling.dao.impl.CustomSortField;

@Component
@Configurable
public abstract class PagingDataProvider<T> implements DataProvider<T>, Sortable2, Filterable 
{
	private static final long serialVersionUID = 5015665477629397068L;
	private static final Logger logger = Logger.getLogger(PagingDataProvider.class.getName());

	// JSF 2.0 EL expression parsing
	//     - finds "myField" in "#{component1.component2.component3.myField}"
	private static final String patternString = "\\.([^\\.]+)}$";
	private static Pattern pattern = Pattern.compile(patternString);

	private List<FilterField> filterFields;
	private List<SortField2> sortFields;

	@Override
	public List<FilterField> getFilterFields()
	{
		return filterFields;
	}
	
	@Override
	public void setFilterFields(List<FilterField> filterFields)
	{
		for (FilterField field: filterFields)
		{
			if (logger.isLoggable(Level.FINEST))
			{
				String buffer = "setFilterFields():: ";
				buffer += " expression(" + field.getExpression().getExpressionString() + "), ";
				if (field instanceof ExtendedFilterField)
					buffer += " filterValue(" + ((ExtendedFilterField)field).getFilterValue() + ")";
				logger.finest(buffer);
			}
		}
		this.filterFields = filterFields;
	}

	@Override
	public List<SortField2> getSortFields()
	{
		return sortFields;
	}

	@Override
	public void setSortFields(List<SortField2> sortFields)
	{
		for (SortField2 field: sortFields)
		{
			if (logger.isLoggable(Level.FINEST))
			{
				String buffer = "setSortFields():: ";
				buffer += " expression(" + field.getExpression().getExpressionString() + "), ";
				buffer += " ordering(" + field.getOrdering() + ")";
				logger.finest(buffer);
			}
		}
		this.sortFields = sortFields;
	}







	/**
	 * JSF 2.0 EL expression "parsing" - finds and returns "myField" in "#{component1.component2.component3.myField}".
	 *    Raises RuntimeException otherwise (means: wrong usage in XHTML).
	 * 
	 * @param expr
	 * @return
	 */
	private static final String getLastFieldFromExpression(Expression expr)
	{
		Matcher mat = pattern.matcher(expr.getExpressionString());
		if (!mat.find())
			throw new RuntimeException("getLastFieldFromExpression():: Pattern(\""+patternString+"\") failed on \"" + expr.getExpressionString() + "\"");
		return mat.group(1);
	}

	/**
	 * Gets filter values usable in non-web application area (uses no JSF/RichFaces classes)
	 * 
	 * @return
	 */
	public List<CustomFilterField> getCustomFilterFields()
	{
		List<CustomFilterField> newFields = new ArrayList<CustomFilterField>();
		if (filterFields != null)
		{
			for (FilterField field : filterFields)
			{
				if (field == null)
					continue;
				CustomFilterField newField = new CustomFilterField();
				if (field instanceof ExtendedFilterField)
				{
					String value = ((ExtendedFilterField) field).getFilterValue();
					// skip this filter since it filters nothing...
					if (value == null)
						continue;
					newField.setFilterValue(value);
				}
				// get field name from EL expression in XHTML
				// This is quite tricky and unclean part. However, there is no other way how to do it. 
				newField.setPropertyName(getLastFieldFromExpression(field.getExpression()));
				logger.info("getFilterFields2():: converted to field: " + newField);
				newFields.add(newField);
			}
		}
		return newFields;
	}


	/**
	 * Gets sort values usable in non-web application area (uses no JSF/RichFaces classes)
	 * 
	 * @return
	 */
	public List<CustomSortField> getCustomSortFields()
	{
		List<CustomSortField> newFields = new ArrayList<CustomSortField>();
		if (filterFields != null)
		{
			for (SortField2 field : sortFields)
			{
				if (field == null)
					continue;
				CustomSortField newField = new CustomSortField();
				if (Ordering.ASCENDING.equals(field.getOrdering()))
				{
					newField.setOrdering(CustomSortField.ASCENDING);
				}
				else if (Ordering.DESCENDING.equals(field.getOrdering()))
				{
					newField.setOrdering(CustomSortField.DESCENDING);
				}
				else
				{
					// UNORDERED => skip it
					continue;
				}
				// Get field name from EL expression in XHTML
				// This is quite tricky and unclean part. However, there is no other way how to do it. 
				newField.setPropertyName(getLastFieldFromExpression(field.getExpression()));
				logger.info("getCustomSortFields():: converted to field: " + newField);
				newFields.add(newField);
			}
		}
		return newFields;
	}
}